/*
 *   This File is Part of CuteOgre Game Engine
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *	Copyright 2010 Alexandre Pires ( c.alexandre.pires@gmail.com )
 *  Created on: 2010/09/04
 */

#include "Gui/StyleSheet.h"
#include "Gui/Style.h"
#include "LogManager.h"
#include "OGRE/OgreResourceManager.h"

StyleSheet::StyleSheet ( const QString& rFile ) {

	parseFile( rFile );
	mName = rFile;
}

StyleSheet::~StyleSheet () {

	mStyles.clear();
}

Style StyleSheet::getStyle ( const QString& rName ) const {

	if ( mStyles.find ( rName ) == mStyles.end () ) {
		LogManager::message(
				QString ( "StyleSheet:: Couldn't find Style %1!" ).arg ( rName ) );
		return Style();
	}

	return mStyles[rName];
}

QString StyleSheet::readFileContents ( const QString& rFile ) {

	Ogre::ResourceGroupManager* resourceManager =
			&Ogre::ResourceGroupManager::getSingleton ();

	if ( !resourceManager->resourceExists ( "StyleSheets", rFile.toStdString () ) ) {
		LogManager::message( QString (
				"StyleSheet::Cannot find %1 in styles directory" ).arg ( rFile ) );
		return QString::null;
	}

	Ogre::DataStreamPtr scriptStream = resourceManager->openResource (
			rFile.toStdString () );

	QString contents;

	while ( !scriptStream->eof () ) {

		QString line = QString ().fromStdString (
				scriptStream->getLine ( false ) );

		contents.append ( line + "\n" );
	}

	return contents;
}

void StyleSheet::parseFile ( const QString& rFile ) {

	QString content = readFileContents ( rFile );

	if ( content.isNull () || content.isEmpty () )
		return;

	bool end = content.length () == 0;

	int col = 1;
	int line = 1;
	int offset = 0;
	QString currentSelector;

	while ( !end ) {

		QChar ch = content[offset];

		if ( ch == '@' ) {

			if ( !parseDirective ( rFile, content, &offset, &line, &col ) ) {
				break;
			}


			col++;
			end = ( offset++ == content.length () );
			continue;

		} else if ( ch == '/' ) {

			if ( offset++ == content.length () ) {
				LogManager::message(
						QString (
								"StyleSheet::Unexpected end of file when parsing %1" ).arg (
								rFile ) );
				break;
			}

			col++;

			if ( content[offset] != '*' ) {
				LogManager::message(
						QString (
								"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
								rFile ).arg ( line ).arg ( col ) );
				break;
			}

			if ( !parseComment ( rFile, content, &offset, &line, &col ) ) {
				break;
			}

			col++;
			end = ( offset++ == content.length () );
			continue;

		} else if ( ch == '{' ) {

			if ( mStyles.find ( currentSelector ) == mStyles.end () ) {
				mStyles[currentSelector] = Style(currentSelector);
			}

			Style style = mStyles[currentSelector];

			if ( !parseSection ( rFile, content, &style, &offset, &line, &col ) ) {
				break;
			}

			mStyles[currentSelector] = style;

			currentSelector.clear ();

			col++;
			end = ( offset++ == content.length () );
			continue;

		} else if ( ch == '\n' ) {

			line++;
			col = 1;
			end = ( offset++ == content.length () );
			continue;

		} else if ( ch == ';' ) {

			LogManager::message( QString (
					"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
					rFile ).arg ( line ).arg ( col ) );
			break;

		} else if ( ch.isSpace () ) {

			col++;
			end = ( offset++ == content.length () );
			continue;

		} else if ( ch.isLetter () || ch.isNumber () || ch == '#' || ch == ':' ) {

			if ( ch.isNumber () && currentSelector.isEmpty () ) {
				LogManager::message(
						QString (
								"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
								rFile ).arg ( line ).arg ( col ) );
			}

			currentSelector.append ( ch );
			col++;
			end = ( offset++ == content.length () );
			continue;
		}

		break;
	}
}

bool StyleSheet::parseDirective ( const QString& rFile,
		const QString& rContents, int* rOffset, int* rLine, int* rCol ) {

	QString directive;

	bool end = ( (*rOffset)++ == rContents.length () );

	while ( !end ) {

		QChar ch = rContents[*rOffset];

		if ( ch == ';' ) {

			LogManager::message(
					QString (
							"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
							rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

			return false;

		} else if ( ch == '/' ) {

			if ( (*rOffset)++ == rContents.length () ) {
				LogManager::message(
						QString (
								"StyleSheet::Unexpected end of file when parsing %1" ).arg (
								rFile ) );
				break;
			}

			(*rCol)++;

			if ( rContents[*rOffset] != '*' ) {
				LogManager::message(
						QString (
								"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
								rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );
				break;
			}

			if ( !parseComment ( rFile, rContents, rOffset, rLine, rCol ) ) {

				LogManager::message( QString (
						"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
						rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

				return false;
			}

			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		} else if ( ch.isSpace () ) {

			if ( directive.isEmpty () ) {

				LogManager::message(
						QString (
								"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
								rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

				return false;
			}

			QString value = parseValue ( rFile, rContents, rOffset, rLine, rCol );

			if ( value.isNull () ) {

				LogManager::message(
						QString (
								"StyleSheet::Error parsing value %1 at line %2, column %3" ).arg (
								rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

				break;

				return true;
			}

			if ( !directive.compare ( "import", Qt::CaseInsensitive ) ) {
				LogManager::message(
						QString (
								"StyleSheet::processing stylesheet %1, import directive not yet implemented!" ).arg (
								rFile ) );
			} else {
				LogManager::message(
						QString (
								"StyleSheet::processing stylesheet %1, ignoring directive %2 with value %3" ).arg (
								rFile ).arg ( directive ).arg ( value ) );
			}

			return true;

		} else if ( ch == '\n' ) {

			LogManager::message(
					QString (
							"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
							rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

			return false;

		} else if ( ch.isLetter () ) {

			directive.append ( ch );


			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;
		}

		LogManager::message( QString (
				"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
				rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

		return false;
	}

	LogManager::message( QString (
			"StyleSheet::Unexpected end of file when parsing %1" ).arg ( rFile ) );

	return false;
}

bool StyleSheet::parseComment ( const QString& rFile, const QString& rContents,
		int* rOffset, int* rLine, int* rCol ) {

	bool end = ( (*rOffset)++ == rContents.length () );

	while ( !end ) {

		QChar ch = rContents[*rOffset];

		if ( ch == '*' ) {

			if ( (*rOffset)++ == rContents.length () ) {

				LogManager::message(
						QString (
								"StyleSheet::Unexpected end of file when parsing %1" ).arg (
								rFile ) );
				return false;
			}

			(*rCol)++;

			if ( rContents[*rOffset] == '/' ) {
				return true;
			}
		} else if ( ch == '\n' ) {

			(*rLine)++;
			(*rCol) = 1;
			end = ( (*rOffset)++ == rContents.length () );
			continue;
		}

		(*rCol)++;
		end = ( (*rOffset)++ == rContents.length () );
	}

	LogManager::message( QString (
			"StyleSheet::Unexpected end of file when parsing %1" ).arg ( rFile ) );

	return false;
}

bool StyleSheet::parseSection ( const QString& rFile, const QString& rContents,
		Style* rStyle, int* rOffset, int* rLine, int* rCol ) {

	bool end = ( (*rOffset)++ == rContents.length () );
	QString key;

	while ( !end ) {

		QChar ch = rContents[*rOffset];

		if ( ch == '}' ) {

			return true;

		} else if ( ch == '\n' ) {

			(*rLine)++;
			(*rCol) = 1;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		} else if ( ch.isSpace() ) {

			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		} else if ( ch == ':' ) {

			if ( key.isEmpty() ) {

				LogManager::message( QString (
						"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
						rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

				return false;
			}

			QString value = parseValue ( rFile, rContents, rOffset, rLine, rCol );

			if ( value.isNull () ) {

				LogManager::message(
						QString (
								"StyleSheet::Error parsing value %1 at line %2, column %3" ).arg (
								rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

				break;

				return false;
			}

			rStyle->addAttribute( key, StyleAttribute(value) );
			key.clear();

			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		} else if ( ch == '\n' ) {

			(*rLine)++;
			(*rCol) = 1;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		} else if ( ch.isLetterOrNumber() || ch == '-' ) {

			key.append(ch);
			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		} else if ( ch == '/' ) {

			if ( (*rOffset)++ == rContents.length () ) {
				LogManager::message(
						QString (
								"StyleSheet::Unexpected end of file when parsing %1" ).arg (
								rFile ) );
				break;
			}

			(*rCol)++;

			if ( rContents[*rOffset] != '*' ) {
				LogManager::message(
						QString (
								"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
								rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );
				break;
			}

			if ( !parseComment ( rFile, rContents, rOffset, rLine, rCol ) ) {

				LogManager::message( QString (
						"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
						rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

				return false;
			}

			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;

		}

		LogManager::message( QString (
				"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
				rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

		return false;
	}

	LogManager::message( QString (
			"StyleSheet::Unexpected end of file when parsing %1" ).arg ( rFile ) );

	return false;
}

QString StyleSheet::parseValue ( const QString& rFile, const QString& rContents,
		int* rOffset, int* rLine, int* rCol ) {


	bool end = ( (*rOffset)++ == rContents.length () );
	QString value;

	while ( !end ) {

		QChar ch = rContents[*rOffset];

		if ( ch == ';' ) {

			return value;

		} else if ( ch == '\n' ) {

			LogManager::message( QString (
					"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
					rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

			return QString::null;

		} else if ( ch == '/' ) {

			if ( (*rOffset)++ == rContents.length () ) {
				LogManager::message(
						QString (
								"StyleSheet::Unexpected end of file when parsing %1" ).arg (
								rFile ) );
				break;
			}

			(*rCol)++;

			if ( rContents[*rOffset] == '*' ) {

				if ( !parseComment ( rFile, rContents, rOffset, rLine, rCol ) ) {

					LogManager::message( QString (
							"StyleSheet::Error parsing %1 at line %2, column %3" ).arg (
							rFile ).arg ( (*rLine) ).arg ( (*rCol) ) );

					return QString::null;
				}

			}

			(*rCol)++;
			end = ( (*rOffset)++ == rContents.length () );
			continue;
		}

		value.append(ch);

		(*rCol)++;
		end = ( (*rOffset)++ == rContents.length () );
		continue;
	}

	LogManager::message( QString (
			"StyleSheet::Unexpected end of file when parsing %1" ).arg ( rFile ) );

	return QString::null;
}

